#include "napi/native_api.h"
#include <js_native_api.h>
#include <js_native_api_types.h>
#include "demoClass.h"

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;

}

static napi_value BenchmarkZero(napi_env env, napi_callback_info info)
{
    return nullptr;
}

static napi_value BenchmarkOne(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkTwo(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkThree(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkFour(napi_env env, napi_callback_info info)
{
    size_t argc = 4;
    napi_value args[4] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkFive(napi_env env, napi_callback_info info)
{
    size_t argc = 5;
    napi_value args[5] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkSix(napi_env env, napi_callback_info info)
{
    size_t argc = 6;
    napi_value args[6] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkSeven(napi_env env, napi_callback_info info)
{
    size_t argc = 7;
    napi_value args[7] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkEight(napi_env env, napi_callback_info info)
{
    size_t argc = 8;
    napi_value args[8] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkNine(napi_env env, napi_callback_info info)
{
    size_t argc = 9;
    napi_value args[9] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkTen(napi_env env, napi_callback_info info)
{
    size_t argc = 10;
    napi_value args[10] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    return nullptr;
}

static napi_value BenchmarkCallBack(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    napi_value printCb;
    napi_status status = napi_get_named_property(env, args[0], "print", &printCb);
    if (status != napi_ok) {
        return nullptr;
    }
    napi_value resultValue;
    status = napi_call_function(env, nullptr, printCb, 0, nullptr, &resultValue);
    return nullptr;
}

static napi_value BenchmarkSetJSValue(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    napi_value setValue;
    const char* templateString = "1";
    napi_status status = napi_create_string_utf8(env, templateString, 1, &setValue);
    if (status != napi_ok) {
        return nullptr;
    }
    
    status = napi_set_named_property(env, args[0], "a", setValue);
    if (status != napi_ok) {
        return nullptr;
    }
    
    status = napi_set_named_property(env, args[0], "b", setValue);
    if (status != napi_ok) {
        return nullptr;
    }
    
    status = napi_set_named_property(env, args[0], "c", setValue);
    if (status != napi_ok) {
        return nullptr;
    }
    return nullptr;
}

static napi_value BenchmarkSetLargeArrayValue(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    napi_value setValue;
    napi_status status = napi_create_int32(env, 2, &setValue);
    if (status != napi_ok) {
        return nullptr;
    }
    
    uint32_t size;
    status = napi_get_value_uint32(env, args[1], &size);
    
    for (uint32_t i = 0; i < size; ++i) {
        status = napi_set_element(env, args[0], i, setValue);
        if (status != napi_ok) {
            return nullptr;
        }
    }
    return nullptr;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkZero", nullptr, BenchmarkZero, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkOne", nullptr, BenchmarkOne, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkTwo", nullptr, BenchmarkTwo, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkThree", nullptr, BenchmarkThree, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkFour", nullptr, BenchmarkFour, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkFive", nullptr, BenchmarkFive, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkSix", nullptr, BenchmarkSix, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkSeven", nullptr, BenchmarkSeven, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkEight", nullptr, BenchmarkEight, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkNine", nullptr, BenchmarkNine, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkTen", nullptr, BenchmarkTen, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkCallBack", nullptr, BenchmarkCallBack, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkSetJSValue", nullptr, BenchmarkSetJSValue, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "benchmarkSetLargeArrayValue", nullptr, BenchmarkSetLargeArrayValue, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    DemoClass::Init(env, exports);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version =1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
